﻿using System;
using System.Collections.Generic;

namespace SBToolkit.Expressions
{
    /// <summary>
    /// Default equality comparer.
    /// </summary>
    /// <typeparam name="T">The <see cref="Type">type</see> of the values that are checked for equality.</typeparam>
    public static class DefaultComparer<T>
    {
        /// <summary>
        /// Return default equality comparer for the specified object property.
        /// </summary>
        /// <param name="getValue">The function used to extract value for equality comparison.</param>
        /// <returns>Default equality comparer.</returns>
        public static IEqualityComparer<T> For<V>(Func<T, V> getValue)
        {
            return new DefaultComparer<T, V>(getValue);
        }
    }

    /// <summary>
    /// Default equality comparer.
    /// </summary>
    /// <typeparam name="T">The <see cref="Type">type</see> of the values that are checked for equality.</typeparam>
    /// <typeparam name="V">The <see cref="Type">type</see> of the object that properties are checked for equality.</typeparam>
    public sealed class DefaultComparer<T, V> : IEqualityComparer<T>
    {
        /// <summary>
        /// The function to get object value.
        /// </summary>
        private readonly Func<T, V> getValue;

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultComparer&lt;T, V&gt;"/> class.
        /// </summary>
        /// <param name="getValue">The get value.</param>
        public DefaultComparer(Func<T, V> getValue)
        {
            this.getValue = getValue;
        }

        /// <summary>
        /// Determines whether the specified objects are equal.
        /// </summary>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns>
        /// true if the specified objects are equal; otherwise, false.
        /// </returns>
        public bool Equals(T x, T y)
        {
            return EqualityComparer<V>.Default.Equals(getValue(x), getValue(y));
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        /// <exception cref="T:System.ArgumentNullException">
        /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
        /// </exception>
        public int GetHashCode(T obj)
        {
            var value = getValue(obj);
            if (value == null)
            {
                return 0;
            }

            return EqualityComparer<V>.Default.GetHashCode(value);
        }
    }
}
